#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

//法一：容器法
int main() {
//先加速
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	//输入
	int n;
	if (!(cin >> n)) return 0;
	vector<long long>a(n);
	for (int i = 0; i < n; i++) { cin >> a[i];}

	//找到最大位数
	long long mx = 0;
	for (auto v : a)mx = max( mx,v );
	int maxDigits = (mx == 0) ? 1: (int)to_string(mx).size();

	//提取位数
	long long base = 1;
	for (int i = 0; i < maxDigits; i++) {
	//每一位有0~9共十个数，即十个桶
		vector<vector<long long>>buckets(10);
		for (auto v : a) {
			int digits = (v / base)%10;
			//取数字某一位的通用公式 (v/base)%10
			buckets[digits].push_back(v);
		}
		//每比较完一位数，开始合并桶里的,替换原来的桶里的
		int idx = 0;
		for (int i = 0; i < 10; i++) {
			for (auto v : buckets[i]) a[idx++] = v;
		}
		//输出
		for (int i = 0; i < n; i++) {
			if (i) cout << ' ';//因为行尾无空格就意味着行头无空格，所以干脆让每一个行头前无空格
			cout << a[i];
		}
		cout << endl;
		base *= 10;
	}


	return 0;
}

//法二：链表版
struct Node { int val; Node* next; Node(int v) :val(v), next(nullptr) {} };
void radix_list(Node*& head) {
	if (!head) return;
	int mx = 0;
	for (Node* p = head; p; p = p->next) mx = max(mx, p->val);
	int maxdigit = (mx==0) ? 1 : (int)to_string(mx).size();

	long long base = 1;
	for (int i = 0; i < maxdigit; i++)
	{
		Node* bh[10] = { nullptr };//创建十个桶，某一位数从0到9的十个桶，nullptr表明是空桶
		Node* bt[10] = { nullptr };
		//分配到桶
		for (Node* p = head; p;) {
			//先保存下一个节点
			Node* nxt = p->next;
			p->next = nullptr;//断开p

			int digit = (p->val / base) % 10;
			//尾插法
			if (!bh[digit]) { bh[digit] = bt[digit] = p; }
			else {
				bt[digit]->next = p;
				bt[digit] = p;
			}
			p = nxt;
			
		}

		//合并桶(合并需要尾指针)
		head = nullptr;
		Node* tail = nullptr;
		for (int i = 0; i < 10; i++) {
			if (bh[i]) {
				if (!head) {//处理桶0
					head = bh[i];
					tail = bt[i];
				}
				else {
					tail->next = bh[i];
					tail = bt[i];//更新链表的尾节点
				}
			}
		}
		for (Node* p = head; p;) {
			cout << p->val;
			if (p->next) cout << ' ';
			p = p->next;
		}

		base *= 10;
		cout << endl;
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	int val;
	Node* head = nullptr;
	Node* tail = nullptr;

	for (int i = 0; i < n; i++)
	{
		cin >> val;
		Node* p = new Node(val);//动态分配内存，后面需要释放内存，避免内存泄漏
		//用new在堆上申请了内存，不手动delete就会造成内存泄漏，泄露在长时间/多次用例/循环构造数据时会迅速积累，导致内存占用飙升、程序崩溃

		if (!head) {
			head = tail = p;
		}
		else {
			tail->next = p;
			tail = p;
		}
	}

	radix_list(head);

	while (head) {
		Node* temp = head;
		head = head->next;
		delete temp;
	}
	return 0;
}
